1 /**
2 Copyright: Copyright (c) 2017, Joakim Brännström. All rights reserved.
3 License: MPL-2
4 Author: Joakim Brännström (joakim.brannstrom@gmx.com)
5 
6 This Source Code Form is subject to the terms of the Mozilla Public License,
7 v.2.0. If a copy of the MPL was not distributed with this file, You can obtain
8 one at http://mozilla.org/MPL/2.0/.
9 
10 This is a handy range to interate over either all files from the user OR all
11 files in a compilation database.
12 */
13 module code_checker.compile_db.user_filerange;
14 
15 import logger = std.experimental.logger;
16 
17 import code_checker.compile_db : CompileCommandFilter, CompileCommandDB,
18     parseFlag, SearchResult;
19 import code_checker.types : FileName, AbsolutePath;
20 
21 @safe:
22 
23 struct UserFileRange {
24     import std.typecons : Nullable;
25     import code_checker.compile_db : SearchResult;
26 
27     enum RangeOver {
28         inFiles,
29         database
30     }
31 
32     this(CompileCommandDB db, string[] in_files, string[] cflags,
33             const CompileCommandFilter ccFilter) {
34         this.db = db;
35         this.cflags = cflags;
36         this.ccFilter = ccFilter;
37         this.inFiles = in_files;
38 
39         if (in_files.length == 0) {
40             kind = RangeOver.database;
41         } else {
42             kind = RangeOver.inFiles;
43         }
44     }
45 
46     const RangeOver kind;
47     CompileCommandDB db;
48     string[] inFiles;
49     string[] cflags;
50     const CompileCommandFilter ccFilter;
51 
52     Nullable!SearchResult front() {
53         assert(!empty, "Can't get front of an empty range");
54 
55         Nullable!SearchResult curr;
56 
57         final switch (kind) {
58         case RangeOver.inFiles:
59             if (db.length > 0) {
60                 curr = db.findFlags(FileName(inFiles[0]), cflags, ccFilter);
61             } else {
62                 curr = SearchResult(cflags.dup, AbsolutePath(FileName(inFiles[0])));
63             }
64             break;
65         case RangeOver.database:
66             import std.array : appender;
67 
68             auto tmp = db.payload[0];
69             auto flags = appender!(string[])();
70             flags.put(cflags);
71             flags.put(tmp.parseFlag(ccFilter));
72             curr = SearchResult(flags.data, tmp.absoluteFile);
73             break;
74         }
75 
76         return curr;
77     }
78 
79     void popFront() {
80         assert(!empty, "Can't pop front of an empty range");
81 
82         final switch (kind) {
83         case RangeOver.inFiles:
84             inFiles = inFiles[1 .. $];
85             break;
86         case RangeOver.database:
87             db.payload = db.payload[1 .. $];
88             break;
89         }
90     }
91 
92     bool empty() @safe pure nothrow const @nogc {
93         final switch (kind) {
94         case RangeOver.inFiles:
95             return inFiles.length == 0;
96         case RangeOver.database:
97             return db.length == 0;
98         }
99     }
100 
101     size_t length() @safe pure nothrow const @nogc {
102         final switch (kind) {
103         case RangeOver.inFiles:
104             return inFiles.length;
105         case RangeOver.database:
106             return db.length;
107         }
108     }
109 }
110 
111 private:
112 
113 import std.typecons : Nullable;
114 
115 /// Find flags for fname by searching in the compilation DB.
116 Nullable!SearchResult findFlags(ref CompileCommandDB compdb, FileName fname,
117         const string[] flags, ref const CompileCommandFilter flag_filter) {
118     import std.file : exists;
119     import std.path : baseName;
120     import std..string : join;
121 
122     import code_checker.compile_db : appendOrError;
123 
124     typeof(return) rval;
125 
126     auto db_search_result = compdb.appendOrError(flags, fname, flag_filter);
127     if (!db_search_result.isNull) {
128         rval = SearchResult(db_search_result.cflags, db_search_result.absoluteFile);
129         logger.trace("Compiler flags: ", rval.cflags.join(" "));
130         return rval;
131     }
132 
133     logger.error("Unable to find any compiler flags for: ", fname);
134     return rval;
135 }